/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package compilador;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.*;
import java.util.Stack;

/**
 *
 * @author Melinton
 */
public class Automata 
{
    //--------------------------------------------------------------------------
    // CONSTANTES
    //--------------------------------------------------------------------------
    public static final int AFD = 0;
    public static final int AFN = 1;
    
    
    //--------------------------------------------------------------------------
    // ATRIBUTOS
    //--------------------------------------------------------------------------
    public int tipo;
    public ArrayList<Estado> estados;
    public Estado estadoInicial;
    public ArrayList<Estado> estadosFinales;
    public ArrayList<Funcion> funciones;  
    public ArrayList<String> lenguaje;
    public Stack<Funcion> seguimientoSimulacion;
    
    //--------------------------------------------------------------------------
    // CONSTRUCTOR  
    //--------------------------------------------------------------------------
    public Automata(Estado eInicial)
    {
        tipo = AFN;
        estados = new ArrayList();
        estadoInicial = eInicial;
        estadosFinales = new ArrayList();
        funciones = new ArrayList();
        lenguaje = new ArrayList();
        seguimientoSimulacion = new Stack();
    }
    
    public Automata(Estado initialEst, ArrayList<Estado> est, ArrayList<Estado> finales, ArrayList<Funcion> func, ArrayList<String> leng, int type)
    {
        estados = est;
        estadosFinales = finales;
        funciones = func;
        lenguaje = leng;
        estadoInicial = initialEst;
        tipo = type;
    }        
    
    //--------------------------------------------------------------------------
    // MÉTODOS  
    //--------------------------------------------------------------------------        
    public ArrayList<Estado> mover(ArrayList<Estado> q, String simbolo )
    {
        //Funcion de mover para AFN
        ArrayList<Estado> u = new ArrayList();
        for(int i = 0; i < q.size(); i++)
        {
            for(int w = 0; w < funciones.size(); w++)
            {
                if(funciones.get(w).estado.equals(q.get(i)) && funciones.get(w).simbolo.equals(simbolo))
                {
                    u.add(funciones.get(w).estadoResultante);
                    seguimientoSimulacion.push(funciones.get(w));
                }
            }
        }
        return u;
    }
    
    public ArrayList cerraduraEpsilon(ArrayList<Estado> T)
    {
        ArrayList<Estado> cerraduraE = new ArrayList();
        Stack<Estado> pila = new Stack();
        Estado t;
        Funcion func = null;
        for(int i = 0; i < T.size(); i++)
        {
            pila.push(T.get(i));
            cerraduraE.add(T.get(i));
        }
        while(!pila.isEmpty())
        {
            t = pila.pop();            
            for(int u = 0; u < funciones.size(); u++)
            {
                if(funciones.get(u).estado.equals(t) && funciones.get(u).simbolo.equals("¬"))
                {                    
                    if(!(cerraduraE.contains((Estado) funciones.get(u).estadoResultante)))
                    {
                        cerraduraE.add(funciones.get(u).estadoResultante);
                        pila.push(funciones.get(u).estadoResultante);
                        func = funciones.get(u);
                    }
                }
            }
        }
        ArrayList estYFunc = new ArrayList();
        estYFunc.add(cerraduraE);
        estYFunc.add(func);
        return estYFunc;
    }
    
    public Automata toAFD() 
    {
        ArrayList<Funcion> funcs = new ArrayList();
        Estado inicial = new Estado(Integer.toString(0));
        ArrayList<Estado> estads = new ArrayList();
        ArrayList<Estado> finales = new ArrayList();
        ArrayList<Estado> q_0f = new ArrayList();
        q_0f.add(estadoInicial);
        ArrayList<Estado> q_0d = cerraduraEpsilon(q_0f);
        HashMap Q_D = new HashMap();
        Q_D.put(inicial, q_0d);
        for(int i = 0; i < Q_D.size(); i++)
        {
            ArrayList<Estado> q = new ArrayList();
            Iterator states = Q_D.keySet().iterator();            
            Estado actual = null;
            Estado fin = null;
            while(states.hasNext())
            {
                Estado piv = (Estado) states.next();                
                if(piv.valor.equals(Integer.toString(i)))
                {
                    q = (ArrayList<Estado>) Q_D.get(piv);
                    actual = piv;
                }
            }
            for(int u=0; u < lenguaje.size(); u++)
            {
                ArrayList<Estado> U_F = cerraduraEpsilon(mover(q,lenguaje.get(u)));
                states = Q_D.keySet().iterator();
                boolean existe = false; 
                while(states.hasNext() && !existe)
                {
                    Estado piv = (Estado) states.next();                    
                    if(compararArrays(U_F,(ArrayList) Q_D.get(piv)))
                    {
                        existe = true;
                        fin = piv;
                    }
                }
                if(existe)
                {
                    Funcion func = new Funcion(actual, lenguaje.get(u), fin);
                    funcs.add(func);
                }
                else if(!U_F.isEmpty())
                {
                    Estado est = new Estado(Integer.toString(Q_D.size()));
                    Funcion func = new Funcion(actual, lenguaje.get(u), est);
                    funcs.add(func);
                    Q_D.put(est, U_F);
                }
            }
            boolean prueba = false;
            for(int h = 0; h < funcs.size() && !prueba; h++)
            {                
                if(funcs.get(h).estado.equals(actual) && funcs.get(h).simbolo.equals("¬"))
                    prueba = true;
            }
            if(!prueba)
                funcs.add(new Funcion(actual, "¬", actual));
        }        
            Estado ultimo = estadosFinales.get(0);
            Iterator estates = Q_D.keySet().iterator();
            while(estates.hasNext())
            {
                Estado piv = (Estado) estates.next();
                estads.add(piv);
                ArrayList<Estado> estds = (ArrayList<Estado>) Q_D.get(piv); 
                if (estds.contains(ultimo))
                    finales.add(piv);
            }              
        Automata AFD = new Automata(inicial, estads, finales, funcs, lenguaje, Automata.AFD);
        AFD.lenguaje = lenguaje;
        return AFD;
    }
    
     public void anhadirFuncion(Funcion func)
    {
        funciones.add(func);
    }
    
    public void anhadirEstadoFinal(Estado fin)
    {
        estadosFinales.add(fin);
    }
    
    public void anhadirAlLenguaje(ArrayList<String> leng)
    {
        for(int i = 0; i < leng.size(); i++)
        {
            if(!lenguaje.contains(leng.get(i)))
                lenguaje.add(leng.get(i));
        }
        
    }
       
    public boolean compararArrays(ArrayList a, ArrayList b)
    {
        if(a.containsAll(b) && a.size()==b.size())
            return true;
        else
            return false; 
    }
}
